package part6

import (
	"bytes"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"strconv"
	"strings"
	"time"
)

//区块
type Block struct {
	Timestamp       int64         //时间戳
	Transactions    []Transaction //数据
	TransactionHash []byte        //交易的hash值
	PrevBlockHash   []byte        //前一个区块的hash
	Hash            []byte        //自己的hash
	Nonce           int
	Height          int
}

//区块的hash= hash(PrevBlockHash + Data + timestamp)
func (b *Block) SetHash() {
	timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
	headers := bytes.Join([][]byte{b.PrevBlockHash, b.HashTransactions(), timestamp}, []byte{})
	hash := sha256.Sum256(headers)

	b.Hash = hash[:]
}

//hash字符串
func (b *Block) GetHashString() string {
	return hex.EncodeToString(b.Hash)
}

//prevHash字符串
func (b *Block) GetPrevHashString() string {
	return hex.EncodeToString(b.PrevBlockHash)
}

//新建一个区块
func NewBlock(transactions []Transaction, prevBlockHash []byte, height int) *Block {
	block := &Block{time.Now().Unix(), transactions, nil, prevBlockHash, []byte{}, 0, height}
	txHash := block.HashTransactions()
	block.TransactionHash = txHash

	pow := NewProofOfWork(block)
	nonce, hash := pow.Run()

	block.Hash = hash[:]
	block.Nonce = nonce

	return block
}

//序列化交易list
func (b *Block) HashTransactions() []byte {
	var txHashes [][]byte
	var txHash [32]byte

	for _, tx := range b.Transactions {
		txHashes = append(txHashes, tx.ID)
	}
	txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))

	return txHash[:]
}

//转字符串
func (block Block) String() string {
	var lines []string

	lines = append(lines, fmt.Sprintf("--------------------Block[%d]--------------------", block.Height))
	lines = append(lines, fmt.Sprintf("     Height:         %d", block.Height))
	lines = append(lines, fmt.Sprintf("     Hash:           %s", block.GetHashString()))
	lines = append(lines, fmt.Sprintf("     txHash:         %s", block.TransactionHash))
	lines = append(lines, fmt.Sprintf("     PrevBlockHash:  %s", block.GetPrevHashString()))
	lines = append(lines, fmt.Sprintf("     Timestamp:      %d", block.Timestamp))
	lines = append(lines, fmt.Sprintf("     Nonce:          %d", block.Nonce))
	// lines = append(lines, fmt.Sprintf("     Transactions:   "))
	for _, t := range block.Transactions {
		lines = append(lines, fmt.Sprint(t))
	}

	return strings.Join(lines, "\n")
}
